package mbsl.ui;

import java.util.ArrayList;
import java.util.List;

import mbsl.system.Core;
import mbsl.util.Delayer.SWTDelayer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;

/**
 * A simple search panel with a text field.
 * <p>
 * The field employs a Delayer, which will cause the system to wait until the user has stopped writing
 * before it starts searching.
 */
public class SearchPanel {
	public String getText() {
		String text = mSearchField.getText();
		
		if (text.equals(SEARCH_FIELD_DEFAULT_TEXT))
			return "";
		else
			return text;
	}
	public void setText(String pText) {
		mSearchField.setText(pText);
	}
	
	/** Adds a listener to which events will be sent when it's time to search. */
	public void addListener(Listener pListener) {
		mListeners.add(pListener);
	}
	/**
	 * Adds a search panel to the passed Composite.
	 * <p>
	 * Style can be added by calling {@link #getWidget()}.  The default search delay will be used.
	 * @param pParent The Composite to add the panel to.
	 */
	public SearchPanel(Composite pParent) {
		this(pParent, SEARCH_FIELD_DEFAULT_ACTION_DELAY);
	}
	/**
	 * Adds a search panel to the passed Composite.
	 * <p>
	 * Style can be added by calling {@link #getWidget()}.
	 * @param pParent The Composite to add the panel to.
	 * @param pDelay The search delay in milliseconds.  If this is less than or equal to zero, there will be no delay.
	 */
	public SearchPanel(Composite pParent, int pDelay) {
		mSearchField = new Text(pParent, SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL | SWT.ICON_SEARCH);
		mListeners = new ArrayList<Listener>();
		
		if (pDelay > 0)
			mDelayer = new SWTDelayer(new ListenerNotifier(), pDelay, pParent.getDisplay());

		mFocusedColour = new Color(mSearchField.getDisplay(), SEARCH_FIELD_FOCUSED_COLOR[0], SEARCH_FIELD_FOCUSED_COLOR[1], SEARCH_FIELD_FOCUSED_COLOR[2]);
		mDefaultColour = new Color(mSearchField.getDisplay(), SEARCH_FIELD_DEFAULT_COLOR[0], SEARCH_FIELD_DEFAULT_COLOR[1], SEARCH_FIELD_DEFAULT_COLOR[2]);
		
		mSearchField.setText(SEARCH_FIELD_DEFAULT_TEXT);
		mSearchField.addFocusListener(new FocusListener());
		mSearchField.setForeground(mDefaultColour);
		mSearchField.addModifyListener(new ModifyListener() {
			@Override
			public void modifyText(ModifyEvent pE) {
				delay();
			}
		});
		mSearchField.addKeyListener(new KeyAdapter() {
			public void keyReleased(KeyEvent e) {
				if (e.character == SWT.ESC)
					mSearchField.setText("");
				
				delay();
			}
		});
		mSearchField.addDisposeListener(new DisposeListener() {
			@Override
			public void widgetDisposed(DisposeEvent pE) {
				if (mDelayer != null)
					mDelayer.dispose();
			}
		});
	}
	/** Returns the search panel Control. */
	public Control getWidget() {
		return mSearchField;
	}
	/** Starts a DelayThread, unless one already is active, in which case it just resets the existing one's timer. */
	private void delay() {
		if (mDelayer.isStarted())
			mDelayer.interrupt();
		else
			mDelayer.start();
	}
	/**
	 * A delay thread which waits for {@link SearchPanel#SEARCH_FIELD_DEFAULT_ACTION_DELAY} milliseconds, and then
	 * notifies the search field's listeners.
	 * <p>
	 * The thread can be delayed by calling interrupt, which will reset its timer.
	 */
	private class ListenerNotifier implements Runnable {
		@Override
		public synchronized void run() {
			for (Listener listener : mListeners)
				listener.handleEvent(new Event());
		}
	}
	/** 
	 * A FocusListener implementation with some special functionality for changing the look of the
	 * search field under certain conditions.
	 * <ul>
	 * <li>If focus is gained, and its contents are equal to {@link SearchPanel#SEARCH_FIELD_DEFAULT_TEXT}, it
	 * will clear it and set the focused colour.  If its contents aren't equal to {@link SearchPanel#SEARCH_FIELD_DEFAULT_TEXT},
	 * it will select all the text in the field.
	 * <li>If focus is lost, and the field is empty, it will set the unfocused colour, and add {@link SearchPanel#SEARCH_FIELD_DEFAULT_TEXT}.
	 * </ul>
	 */
	private class FocusListener implements org.eclipse.swt.events.FocusListener {
		@Override
		public void focusGained(FocusEvent pE) {
			if (mSearchField.getText().equals(SearchPanel.SEARCH_FIELD_DEFAULT_TEXT)) {
				mSearchField.setText("");
				mSearchField.setForeground(mFocusedColour);
			} else
				mSearchField.selectAll();
		}
		@Override
		public void focusLost(FocusEvent pE) {
			if (mSearchField.getText().isEmpty()) {
				mSearchField.setForeground(mDefaultColour);
				mSearchField.setText(SearchPanel.SEARCH_FIELD_DEFAULT_TEXT);
			}
		}
	}
	
	/** The delay between last input and the search, in milliseconds. */
	private static final int SEARCH_FIELD_DEFAULT_ACTION_DELAY = 400;
	 /** The text to appear in the text field when it isn't in use. */
	private static final String SEARCH_FIELD_DEFAULT_TEXT = Core.tp.get("ui.SearchPanel.default");
	/** The colour of the text in the text field when it isn't in use. */
	private static final int[] SEARCH_FIELD_DEFAULT_COLOR = {180, 180, 180};
	/** The colour of the text in the text field when it is focused. */
	private static final int[] SEARCH_FIELD_FOCUSED_COLOR = {0, 0, 0};
	private Color mFocusedColour;
	private Color mDefaultColour;
	private Text mSearchField;
	private List<Listener> mListeners;
	private SWTDelayer mDelayer;
}